import React, { useState, useEffect, useRef, useCallback } from 'react';

import './App.scss';

/**
 * @Title 马里奥兄弟3 - 老虎机游戏
 * @Author 小肚肚肚肚肚哦
 * @Description 操作规则：通过控制三个可活动面板，使其图案对应，便可获得奖励。
 */

function App() {
	const [activeRowIndex, setActiveRowIndex] = useState(0);
	const [rows, setRows] = useState([
		{
			name: 'top',
			index: 0,
			value: 0,
			endValue: 0,
			speed: 200,
			isRunning: true,
			key: Math.random(),
			direction: 'ltr',
		},
		{
			name: 'center',
			value: 0,
			index: 1,
			endValue: 0,
			speed: 200,
			isRunning: true,
			key: Math.random(),
			direction: 'rtl',
		},
		{
			name: 'bottom',
			value: 0,
			index: 2,
			endValue: 0,
			speed: 200,
			isRunning: true,
			key: Math.random(),
			direction: 'ltr',
		},
	]);
	const [prize, setPrize] = useState('none');

	const handleClick = async e => {
		if (
			(e.type === 'keypress' && e.keyCode === 32) ||
			e.type === 'touchstart'
		) {
			// 还有活动的面板
			if (activeRowIndex < rows.length) {
				// 设置移动标志位
				await cancelInterval(activeRowIndex);
				// 设置终值
				await setEndValue(activeRowIndex, rows[activeRowIndex].value);
				// 获取奖励
				await determinePrize();
			}
			// 更新行状态
			updateActiveRow();
		}
	};

	useEffect(() => {
		// 移动端 - 触摸屏幕
		document.body.addEventListener('touchstart', handleClick);
		// pc端 - 空格键
		window.addEventListener('keypress', handleClick);

		return () => {
			document.body.removeEventListener('touchstart', handleClick);
			window.removeEventListener('keypress', handleClick);
		};
	}, [handleClick]);

	// 递增要停止的行的游标 或者 重置游戏
	const updateActiveRow = () => {
		if (activeRowIndex < rows.length) {
			setActiveRowIndex(activeRowIndex + 1);
		} else {
			resetGame();
		}
	};

  /**
   * 分值：星星 1, 花 0，蘑菇 2 （根据图片顺序累加出来的）
   * 马里奥老虎机将人规则：全星星5up，全花3up，全蘑菇2up
   */
	const determinePrize = async () => {
		const endValues = rows.map(row => row.endValue);

		const currentPrize = endValues.every(item => item === 1) ? 0 : 
      (endValues.every(item => item === 0) ? 1 : 
      (endValues.every(item => item === 2) ? 2 : 3));

		await setPrize(currentPrize);
	};

	const resetGame = () => {
		const newRows = rows.map(row => {
			// 重置key
			row.key = Math.random();
			// 定时器开启
			row.isRunning = true;
			return row;
		});

    // 函数式组件中，重新设置map前的数组，不能使map中的组件重新渲染，需要监听 isRunning 的变化
		setRows([...newRows]);
		setActiveRowIndex(0);
	};

	const setRotatingValue = (index, value) => {
    setRows(originRows => {
      const row = originRows[index];
      row.value = value;
      return [...originRows];
    });
	};

	const setEndValue = async (index, value) => {
		const row = rows[index];
		row.endValue = value;

		await setRows([...rows]);
	};

	const cancelInterval = async index => {
		const row = rows[index];
		row.isRunning = false;

		await setRows([...rows]);
	};

	const rowsEntry = rows.map(row => {
		return (
			<Row
				name={row.name}
				index={row.index}
				key={row.index}
				data={{ activeRowIndex, rows, prize }}
				setRotatingValue={setRotatingValue}
				isRunning={row.isRunning}
				speed={row.speed}
				direction={row.direction}
			/>
		);
	});

	return (
		<div>
			<div className="viewport">
				<div className="game">
					<div className="rows">{rowsEntry}</div>
				</div>
				<Results shown={activeRowIndex === 3} prize={prize} />
			</div>
		</div>
	);
}

function Row({
	name,
	index,
	data,
	setRotatingValue,
	isRunning,
	speed,
	direction,
}) {
	const [value, setValue] = useState(0);
	const [timer, setTimer] = useState(null);

  const propsRef = useRef({
    isRunning,
    direction,
    timer,
    value,
    index
  });

  useEffect(() => {
    propsRef.current = {
      isRunning,
      direction,
      timer,
      value,
      index
    }
  }, [isRunning, direction, timer, value, index]);

  useEffect(() => {
    if (isRunning) {
      // 此时表示从停止又切回了运行
      const interval = setInterval(counterIntervalFunction, speed);
      setTimer(interval);
    }
  }, [isRunning]);

  // setInterval 中如何拿到 state 的实时变化值: 使用 ref 来记录
  const counterIntervalFunction = useCallback(() => {
    const props = propsRef.current || {};

		if (props.isRunning && props.direction === 'ltr') {
			// hooks 在 setInterval中设置state, 必须是回调形式，不然不生效
      const newValue = props.value < 2 ? props.value + 1 : 0;
			setValue(value => newValue);
			setRotatingValue(props.index, newValue);
		} else if (props.isRunning && props.direction === 'rtl') {
      const newValue = props.value > 0 ? props.value - 1 : 2;
			setValue(value => newValue);
			setRotatingValue(props.index, newValue);
		} else {
			clearCounterInterval(props.timer);
		}
	}, [isRunning, direction, setRotatingValue]);

	const clearCounterInterval = (timer) => {
		clearInterval(timer);
	};

	return (
		<div className={'row ' + (index === data.activeRowIndex ? 'active' : '')}>
			<div
				className={'columns columns-' + name}
				style={{
					animationName: direction + '-transition-' + value,
					animationDuration: speed + 'ms',
				}}
			></div>
		</div>
	);
}

function Results({ shown, prize }) {
	const messages = ['5UP', '3UP', '2UP', 'No Prize'];

	const classList = 'results ' + (shown ? 'shown' : '');

	return <div className={classList}>{messages[prize]}</div>;
}

export default App;
